/**
 * Copyright 2015-2016 eyaomall.cn
 * All rights reserved.
 *
 * @project
 * @author jackey
 * @version 1.0
 * @date 2016-01-06
 */
package com.eyaomall.wxmall.controller;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.xmlbeans.BindingConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.eyaomall.wxmall.common.error.BECode;
import com.eyaomall.wxmall.common.helper.MemberHelper;
import com.eyaomall.wxmall.common.util.IPAddressUtil;
import com.eyaomall.wxmall.common.util.XmlUtils;
import com.eyaomall.wxmall.model.po.Order;
import com.eyaomall.wxmall.model.po.OrderGoods;
import com.eyaomall.wxmall.model.po.Shop;
import com.eyaomall.wxmall.model.pojo.ReqPay;
import com.eyaomall.wxmall.service.OrderService;
import com.eyaomall.wxmall.service.ShopService;
import com.github.pagehelper.StringUtil;
import com.lycheeframework.core.cmp.api.error.SECode;
import com.lycheeframework.core.cmp.api.http.GsnData;
import com.lycheeframework.core.cmp.api.http.GsnResponse;
import com.lycheeframework.core.common.helper.GlobEnv;
import com.lycheeframework.core.common.util.DateUtil;
import com.lycheeframework.core.common.util.encrypt.MD5;
import com.lycheeframework.core.exception.ErrorCodeException;

/**
 * 支付
 * 
 * @author pro
 *
 */
@Controller
@RequestMapping("pay")
public class PayController {

	@Autowired
	private OrderService orderService;

	@Autowired
	private ShopService shopService;
	
	private static Logger LOG = LoggerFactory.getLogger(PayController.class);

	@RequestMapping("pay")
	public String pay(Integer orderId, String redirectUrl, Map<String, Object> data) {
		if (orderId == null || orderId <= 0)
			throw new ErrorCodeException(BECode.order_4000001);

		data.put("random", RandomStringUtils.random(4, "1234567890"));
		data.put("orderId", orderId);
		data.put("redirectUrl", redirectUrl);
		return "order/pay";
	}

	/**
	 * 支付页面
	 * 
	 * @param orderId
	 * @param request
	 * @param data
	 * @return
	 * @throws IOException
	 */
	@RequestMapping("index")
	public @ResponseBody GsnData pay(Integer orderId, HttpServletRequest request) throws IOException {
		if (orderId == null || orderId <= 0)
			throw new ErrorCodeException(BECode.order_4000001);

		// 查询我的订单信息
		Order order = orderService.queryById(orderId);
		
		// 查询我的订单产品信息
		List<OrderGoods> orderGoodsList = orderService.listOrderGoods(order.getId());

		ReqPay reqPay = initData(order, orderGoodsList, request);
		Map<String, Object> payMap = getData(reqPay);

		// 拼接签名
		StringBuilder sign = new StringBuilder();
		Set<String> keys = payMap.keySet();
		for (String key : keys) {
			Object val = payMap.get(key);
			if (val != null) {
				sign.append(key);
				sign.append("=");
				sign.append(val);
				sign.append("&");
			}
		}

		// 缺微信支付平台key的秘钥
		Shop shop = shopService.queryById(order.getShopId());
		String key = shop.getWxPayKey();
		String tmpSign = sign.toString() + "key=" + key;

		tmpSign = MD5.encryptMD5(tmpSign).toUpperCase();
		reqPay.setSign(tmpSign);

		String s = XmlUtils.createXml(reqPay);
		File xmlFile = new File(GlobEnv.get("tmp.file"));
		FileUtils.writeStringToFile(xmlFile, s, Charset.forName("UTF-8"));

		Map<String, String> jsonData = new TreeMap<String, String>();
		CloseableHttpClient client = HttpClientBuilder.create().build();
		HttpPost post = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
		post.setEntity(new FileEntity(xmlFile));
		HttpResponse response = client.execute(post);
		if (response.getStatusLine().getStatusCode() == 200) {
			String xmlRes = EntityUtils.toString(response.getEntity(), "UTF-8");
			LOG.error("统一下单接口正常报文：" + xmlRes);
			Map<String, String> resMap = XmlUtils.read(xmlRes);
			if (resMap == null)
				throw new ErrorCodeException(SECode.s_100001);

			if ("FAIL".equalsIgnoreCase(resMap.get("return_code"))) {
				LOG.error("统一下单接口日志：" + xmlRes);
				throw new ErrorCodeException(BECode.pay_5000001);
			}

			jsonData.put("package", "prepay_id=" + resMap.get("prepay_id"));
			jsonData.put("signType", "MD5");
			jsonData.put("appId", resMap.get("appid"));

		} else {
			String xmlRes = EntityUtils.toString(response.getEntity(), "UTF-8");
			LOG.error("统一下单接口错误日志：" + xmlRes);
		}

		jsonData.put("timeStamp", String.valueOf(new Date().getTime()));
		String nonceStr = RandomStringUtils.random(8, "1234567890qwertyuiopasdfghjklzxcvbnm");
		jsonData.put("nonceStr", nonceStr);
		StringBuilder paySign = new StringBuilder();
		Set<String> finalKey = jsonData.keySet();
		for (String str : finalKey) {
			String val = jsonData.get(str);
			if (StringUtil.isEmpty(val))
				continue;

			paySign.append(str);
			paySign.append("=");
			paySign.append(val);
			paySign.append("&");
		}

		String jsPay = paySign.toString() + "key=" + key;
		String jsSign = MD5.encryptMD5(jsPay).toUpperCase();
		jsonData.put("paySign", jsSign);

		LOG.error("jsonData" + jsonData);

		return GsnResponse.toData(jsonData);
	}

	/**
	 * 初始化data数据
	 * 
	 * @param order
	 * @param orderGoodsList
	 * @param request
	 * @return
	 */
	private ReqPay initData(Order order, List<OrderGoods> orderGoodsList, HttpServletRequest request) {
		String randomStr = RandomStringUtils.random(6, "1234567890QWERTYUIOPASDFGHJKLZXCVBNM");
		Shop shop = shopService.queryById(order.getShopId());

		ReqPay reqPay = new ReqPay();
		reqPay.setAppid(shop.getWxAppId());
		reqPay.setMch_id(shop.getWxPayId());
		reqPay.setDevice_info("WEB");
		reqPay.setNonce_str(new Date().getTime() + randomStr);
		reqPay.setBody("橙品汇-商品");

		// 订单编号
		reqPay.setOut_trade_no(order.getOrderNo());
		// 付款类型为人民币
		reqPay.setFee_type("CNY");
		// 订单总金额精确到分
		if(order.getCouponAmount() == null){
			reqPay.setTotal_fee(order.getAmount().multiply(new BigDecimal("100")).intValue());
		}else{
			//实际支付的金额为，订单金额减去优惠券金额
			
			//如果优惠券金额小于除去运费后的金额，实际支付金额为订单减去优惠券金额
			BigDecimal amount = order.getAmount().subtract(order.getCouponAmount());
			if(amount.compareTo(new BigDecimal("0")) <= 0){
				List<OrderGoods> goodsList = orderService.listOrderGoods(order.getId());

				// 根据支付平台apiKey进行回调校验
				orderService.changeStatus(order, goodsList, shop);
				throw new ErrorCodeException(BECode.pay_5000005);
			}
			
			reqPay.setTotal_fee(amount.multiply(new BigDecimal("100")).intValue());
		}
		
		// 支付发起的ip
		reqPay.setSpbill_create_ip(IPAddressUtil.getIpAddr(request));
		// 订单创建时间
		reqPay.setTime_start(DateUtil.formatDateTime(order.getGmtCreate(), "yyyyMMddHHmmss"));

		// 设置订单失效日期为订单创建的三天后
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(order.getGmtCreate());
		calendar.add(Calendar.DAY_OF_YEAR, +3);
		reqPay.setTime_expire(DateUtil.formatDateTime(calendar.getTime(), "yyyyMMddHHmmss"));

		reqPay.setNotify_url(GlobEnv.getWebURL("pay/notify"));
		reqPay.setTrade_type("JSAPI");
		reqPay.setOpenid(MemberHelper.getOpenId());

		return reqPay;
	}

	/**
	 * 支付通知
	 * 
	 * @throws Exception
	 */
	@RequestMapping("notify")
	public @ResponseBody void payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		request.setCharacterEncoding("UTF-8");
		response.setCharacterEncoding("UTF-8");

		File file = new File(GlobEnv.get("tmp.result"));
		FileCopyUtils.copy(request.getInputStream(), new FileOutputStream(file));

		String text = FileUtils.readFileToString(file, Charset.forName("UTF-8"));
		Map<String, String> result = XmlUtils.read(text);

		String orderNo = result.get("out_trade_no");
		String wxApp = result.get("appid");
		Order order = orderService.queryByOrderNo(orderNo);
		if (order == null) {
			LOG.error("支付回调：订单编号：" + orderNo + "不存在");
			return;
		}

		List<OrderGoods> goodsList = orderService.listOrderGoods(order.getId());
		Shop shop = shopService.queryByWxAppId(wxApp);

		// 根据支付平台apiKey进行回调校验
		if (XmlUtils.validResult(text, shop.getWxPayKey())) {
			orderService.changeStatus(order, goodsList, shop);
		}

		PrintWriter writer = response.getWriter();
		writer.write("<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
		writer.flush();
		writer.close();

	}

	/**
	 * 获取
	 * 
	 * @param reqPay
	 * @return
	 */
	private Map<String, Object> getData(ReqPay reqPay) {
		Map<String, Object> map = new TreeMap<String, Object>();
		Field[] fields = ReqPay.class.getDeclaredFields();
		for (Field field : fields) {
			try {
				PropertyDescriptor pd = new PropertyDescriptor(field.getName(), ReqPay.class);
				Method getMethod = pd.getReadMethod();// 获得get方法
				Object o = getMethod.invoke(reqPay);// 执行get方法返回一个Object

				if (o == null)
					continue;

				map.put(field.getName(), o);
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
			}
		}

		return map;
	}
}
